package com.szjz.elasticsearch.eshelper.base.wrapper;

import com.szjz.elasticsearch.eshelper.base.BaseESEntity;
import com.szjz.elasticsearch.eshelper.base.BaseHelper;
import com.szjz.elasticsearch.eshelper.base.wrapper.service.MonthWrapperService;
import com.szjz.elasticsearch.eshelper.page.Page;
import com.szjz.elasticsearch.eshelper.service.ESService;
import com.szjz.elasticsearch.eshelper.wrapper.ESQueryWrapper;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.core.CountRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author 史博
 * @desc 按月分库
 * @date 2021/2/26 11:01 周五
 */

@Slf4j
@Service
public class MonthWrapperServiceImpl<T extends BaseESEntity> extends BaseHelper<T> implements MonthWrapperService<T> {

	@Autowired
	private ESService esService;


	/**
	 * 按照时间分片 保存or更新
	 *
	 * @param t
	 */
	@Override
	public Boolean saveOrUpdateAcMonth(T t) {
		if (t == null) {
			log.warn("【ES saveOrUpdateByDate】t can not be null");
			return false;
		}
		if (StringUtils.isEmpty(t.getId())) {
			log.warn("【ES saveOrUpdateByDate】t'id can not be null");
			return false;
		}
		return saveOrUpdateBatchAcMonth(Collections.singletonList(t));
	}

	/**
	 * 按照时间分片批量插入or更新数据
	 * 按照数据的创建时间来分片
	 * ts中的数据创建时间必须是同一天的
	 * docId = _id
	 *
	 * @param ts
	 */
	@SneakyThrows
	@Override
	public Boolean saveOrUpdateBatchAcMonth(List<T> ts) {
		if (CollectionUtils.isEmpty(ts)) {
			log.warn("【ES saveOrUpdateBatchAcMonth】ts list Can not be empty !");
			return false;
		}
		List<T> crtTimes = ts.stream().filter(t -> t.getCrtTime() == null).collect(Collectors.toList());
		if (!CollectionUtils.isEmpty(crtTimes)) {
			log.warn("【ES saveOrUpdateBatchAcMonth】crtTimes : {}", crtTimes);
			log.warn("【ES saveOrUpdateBatchAcMonth】There are elements in the collection whose crtTime time is null!");
			throw new RuntimeException("There are elements in the collection whose crtTime time is null!");
		}
		List<T> ids = ts.stream().filter(t -> t.getId() == null).collect(Collectors.toList());
		if (!CollectionUtils.isEmpty(ids)) {
			log.warn("【ES saveOrUpdateBatchAcMonth】ids : {}", ids);
			log.warn("【ES saveOrUpdateBatchAcMonth】The attribute 'id' in the collection cannot be null!");
			throw new RuntimeException("The attribute 'id' in the collection cannot be null!");
		}
		BulkRequest bulkRequest = new BulkRequest();
		BulkResponse bulkResponse = null;
		String indexName = getIndexNameAcMonth(ts.get(0).getCrtTime());
		for (T t : ts) {
			t.setDocId(t.getId());
			if (t.getUpdTime() == null) {
				t.setUpdTime(Calendar.getInstance().getTime());
			}
			IndexRequest indexRequest = new IndexRequest(indexName)
				.source(getSourceMap(t));
			UpdateRequest updateRequest = new UpdateRequest(indexName, t.getDocId())
				.doc(indexRequest)
				.docAsUpsert(true);
			bulkRequest.add(updateRequest);
		}
		bulkResponse = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
		assert bulkResponse != null;
		if (bulkResponse.hasFailures()) {
			if (!esService.indexExist(indexName)) {
				Boolean isSuccess = esService.createIndex(indexName, getEntityClass());
				log.info("【ES saveOrUpdateBatchAcMonth】索引库 {} 不存在, 重新创建{}", indexName, isSuccess ? "成功！" : "失败！");
				if (isSuccess) {
					return saveOrUpdateBatchAcMonth(ts);
				} else {
					throw new RuntimeException("【ES saveOrUpdateBatchAcMonth】索引库 " + indexName + " 创建失败");
				}
			} else {
				log.error("【ES saveOrUpdateBatchAcMonth】失败！indexName: {} size: {}", indexName, ts.size());
			}
			return false;
		} else {
			log.debug("【ES saveOrUpdateBatchAcMonth】成功！indexName: {} size: {}", indexName, ts.size());
			return true;
		}
	}

	/**
	 * 通过包装类查询按月分片的索引库集合
	 *
	 * @param wrapper 将要查询的条件封装到wrapper中
	 * @param date    索引库对应的分片时间
	 * @return 查询某一个月之内的数据
	 */
	@Override
	public List<T> selectAcMonth(ESQueryWrapper<T> wrapper, Date date) {
		SearchRequest searchRequestAcMonth = getSearchRequestAcMonth(date, date);
		return getSelectTs(wrapper, searchRequestAcMonth, SIZE);
	}

	/**
	 * 通过包装类查询按月分片的索引库集合
	 *
	 * @param wrapper   将要查询的条件封装到wrapper中
	 * @param yearMonth 年月 格式：yyyy-MM 索引库名称结尾日期格式
	 * @return 查询某一个月之内的数据
	 */
	@Override
	public List<T> selectAcMonth(ESQueryWrapper<T> wrapper, String yearMonth) {
		SearchRequest searchRequestAcMonth = getSearchRequestAcMonth(yearMonth, yearMonth);
		return getSelectTs(wrapper, searchRequestAcMonth, SIZE);
	}

	/**
	 * 通过包装类查询按月分片的索引库集合
	 *
	 * @param wrapper   将要查询的条件封装到wrapper中
	 * @param startDate 开始时间
	 * @param endDate   结束时间
	 * @return 某一时间段（月）的数据集合
	 */
	@Override
	public List<T> selectAcMonth(ESQueryWrapper<T> wrapper, Date startDate, Date endDate) {
		SearchRequest searchRequestAcMonth = getSearchRequestAcMonth(startDate, endDate);
		return getSelectTs(wrapper, searchRequestAcMonth, SIZE);
	}

	/**
	 * 通过包装类查询按天分片的索引库集合
	 *
	 * @param wrapper  将要查询的条件封装到wrapper中
	 * @param startYMD 开始时间 年月 格式：yyyy-MM 索引库名称结尾日期格式
	 * @param endYMD   结束时间 年月 格式：yyyy-MM 索引库名称结尾日期格式
	 * @return 某一时间段的数据集合
	 */
	@Override
	public List<T> selectAcMonth(ESQueryWrapper<T> wrapper, String startYMD, String endYMD) {
		SearchRequest searchRequestAcMonth = getSearchRequestAcMonth(startYMD, endYMD);
		return getSelectTs(wrapper, searchRequestAcMonth, SIZE);
	}

	/**
	 * 通过包装类查询按月分片的索引库集合
	 *
	 * @param wrapper 将要查询的条件封装到wrapper中
	 * @param date    索引库对应的分片时间
	 * @param size    返回结果集的最大数量
	 * @return 某个月之内的数据集合
	 */
	@Override
	public List<T> selectAcMonth(ESQueryWrapper<T> wrapper, Date date, Integer size) {
		SearchRequest searchRequestAcMonth = getSearchRequestAcMonth(date, date);
		return getSelectTs(wrapper, searchRequestAcMonth, size);
	}

	/**
	 * 通过包装类查询按月分片的索引库集合
	 *
	 * @param wrapper   将要查询的条件封装到wrapper中
	 * @param yearMonth 索引库对应的分片时间 年月 格式：yyyy-MM 索引库名称结尾日期格式
	 * @param size      返回结果集的最大数量
	 * @return 某一天的数据集合
	 */
	@Override
	public List<T> selectAcMonth(ESQueryWrapper<T> wrapper, String yearMonth, Integer size) {
		SearchRequest searchRequestAcMonth = getSearchRequestAcMonth(yearMonth, yearMonth);
		return getSelectTs(wrapper, searchRequestAcMonth, size);
	}

	/**
	 * 通过包装类查询按月分片的索引库集合
	 *
	 * @param wrapper   将要查询的条件封装到wrapper中
	 * @param startDate 开始时间
	 * @param endDate   结束时间
	 * @param size      返回结果集的最大数量
	 * @return 某一时间段的数据集合
	 */
	@Override
	public List<T> selectAcMonth(ESQueryWrapper<T> wrapper, Date startDate, Date endDate, Integer size) {
		SearchRequest searchRequestAcMonth = getSearchRequestAcMonth(startDate, endDate);
		return getSelectTs(wrapper, searchRequestAcMonth, size);
	}

	/**
	 * 通过包装类查询按月分片的索引库集合
	 *
	 * @param wrapper  将要查询的条件封装到wrapper中
	 * @param startYMD 开始时间
	 * @param endYMD   结束时间
	 * @param size     返回结果集的最大数量
	 * @return 某一时间段的数据集合
	 */
	@Override
	public List<T> selectAcMonth(ESQueryWrapper<T> wrapper, String startYMD, String endYMD, Integer size) {
		SearchRequest searchRequestAcMonth = getSearchRequestAcMonth(startYMD, endYMD);
		return getSelectTs(wrapper, searchRequestAcMonth, size);
	}

	/*====================================================================*/
	/*=============================== T-PAGE =============================*/
	/*====================================================================*/

	/**
	 * 通过包装类查询按月分片的索引库集合
	 *
	 * @param wrapper 将要查询的条件封装到wrapper中
	 * @param date    索引库对应的分片时间
	 * @return 查询某一个月之内的数据
	 */
	@Override
	public Page<T> pageAcMonth(ESQueryWrapper<T> wrapper, Date date, Integer pageNum, Integer pageSize) {
		SearchRequest searchRequestAcMonth = getSearchRequestAcMonth(date, date);
		return getPage(wrapper, pageNum, pageSize, searchRequestAcMonth);
	}

	/**
	 * 通过包装类查询按月分片的索引库集合
	 *
	 * @param wrapper   将要查询的条件封装到wrapper中
	 * @param yearMonth 年月 格式：2020-02 自然月份对应的索引库结尾日期
	 * @param pageNum   当前页
	 * @param pageSize  每页容量
	 * @return 查询某一个月返回的数据
	 */
	@Override
	public Page<T> pageAcMonth(ESQueryWrapper<T> wrapper, String yearMonth, Integer pageNum, Integer pageSize) {
		SearchRequest searchRequestAcMonth = getSearchRequestAcMonth(yearMonth, yearMonth);
		return getPage(wrapper, pageNum, pageSize, searchRequestAcMonth);
	}

	/**
	 * 通过包装类查询按月分片的索引库集合
	 *
	 * @param wrapper   将要查询的条件封装到wrapper中
	 * @param startDate 开始时间
	 * @param endDate   结束时间
	 * @param pageNum   当前页
	 * @param pageSize  每页容量
	 * @return 某一时间段的数据集合
	 */
	@Override
	public Page<T> pageAcMonth(ESQueryWrapper<T> wrapper, Date startDate, Date endDate, Integer pageNum, Integer pageSize) {
		SearchRequest searchRequestAcMonth = getSearchRequestAcMonth(startDate, endDate);
		return getPage(wrapper, pageNum, pageSize, searchRequestAcMonth);
	}

	/**
	 * 通过包装类查询按月分片的索引库集合
	 *
	 * @param wrapper  将要查询的条件封装到wrapper中
	 * @param startYMD 开始时间
	 * @param endYMD   结束时间
	 * @param pageNum  当前页
	 * @param pageSize 每页容量
	 * @return 某一时间段的数据集合
	 */
	@Override
	public Page<T> pageAcMonth(ESQueryWrapper<T> wrapper, String startYMD, String endYMD, Integer pageNum, Integer pageSize) {
		SearchRequest searchRequestAcMonth = getSearchRequestAcMonth(startYMD, endYMD);
		return getPage(wrapper, pageNum, pageSize, searchRequestAcMonth);
	}

	/*====================================================================*/
	/*================================ MAP ===============================*/
	/*====================================================================*/

	/**
	 * 通过包装类查询按月分片的索引库集合
	 *
	 * @param wrapper 将要查询的条件封装到wrapper中
	 * @param date    索引库对应的分片时间
	 * @return 查询某一个月之内的数据
	 */
	@Override
	public List<Map<String, Object>> selectMapAcMonth(ESQueryWrapper<T> wrapper, Date date) {
		SearchRequest searchRequestAcMonth = getSearchRequestAcMonth(date, date);
		return getSelectMaps(wrapper, searchRequestAcMonth, SIZE);
	}


	/**
	 * 通过包装类查询按月分片的索引库集合
	 *
	 * @param wrapper   将要查询的条件封装到wrapper中
	 * @param yearMonth 年月 格式：2020-02 自然月份对应的索引库结尾日期
	 * @return 集合 map
	 */
	@Override
	public List<Map<String, Object>> selectMapAcMonth(ESQueryWrapper<T> wrapper, String yearMonth) {
		SearchRequest searchRequestAcMonth = getSearchRequestAcMonth(yearMonth, yearMonth);
		return getSelectMaps(wrapper, searchRequestAcMonth, SIZE);
	}

	/**
	 * 通过包装类查询按月分片的索引库集合
	 *
	 * @param wrapper   将要查询的条件封装到wrapper中
	 * @param startDate 开始时间
	 * @param endDate   结束时间
	 * @return 集合 map
	 */
	@Override
	public List<Map<String, Object>> selectMapAcMonth(ESQueryWrapper<T> wrapper, Date startDate, Date endDate) {
		SearchRequest searchRequestAcMonth = getSearchRequestAcMonth(startDate, endDate);
		return getSelectMaps(wrapper, searchRequestAcMonth, SIZE);
	}

	/**
	 * 通过包装类查询按月分片的索引库集合
	 *
	 * @param wrapper  将要查询的条件封装到wrapper中
	 * @param startYMD 开始时间
	 * @param endYMD   结束时间
	 * @return 集合 map
	 */
	@Override
	public List<Map<String, Object>> selectMapAcMonth(ESQueryWrapper<T> wrapper, String startYMD, String endYMD) {
		SearchRequest searchRequestAcMonth = getSearchRequestAcMonth(startYMD, endYMD);
		return getSelectMaps(wrapper, searchRequestAcMonth, SIZE);
	}

	/**
	 * 通过包装类查询按月分片的索引库集合
	 *
	 * @param wrapper   将要查询的条件封装到wrapper中
	 * @param yearMonth 索引库对应的分片时间
	 * @param size      返回结果集的最大数量
	 * @return 集合 map
	 */
	@Override
	public List<Map<String, Object>> selectMapAcMonth(ESQueryWrapper<T> wrapper, String yearMonth, Integer size) {
		SearchRequest searchRequestAcMonth = getSearchRequestAcMonth(yearMonth, yearMonth);
		return getSelectMaps(wrapper, searchRequestAcMonth, size);
	}

	/**
	 * 通过包装类查询按月分片的索引库集合
	 *
	 * @param wrapper 将要查询的条件封装到wrapper中
	 * @param date    索引库对应的分片时间
	 * @param size    返回结果集的最大数量
	 * @return 集合 map
	 */
	@Override
	public List<Map<String, Object>> selectMapAcMonth(ESQueryWrapper<T> wrapper, Date date, Integer size) {
		SearchRequest searchRequestAcMonth = getSearchRequestAcMonth(date, date);
		return getSelectMaps(wrapper, searchRequestAcMonth, size);
	}

	/**
	 * 通过包装类查询按月分片的索引库集合
	 *
	 * @param wrapper  将要查询的条件封装到wrapper中
	 * @param startYMD 开始时间
	 * @param endYMD   结束时间
	 * @param size     返回结果集的最大数量
	 * @return 集合 map
	 */
	@Override
	public List<Map<String, Object>> selectMapAcMonth(ESQueryWrapper<T> wrapper, String startYMD, String endYMD, Integer size) {
		SearchRequest searchRequestAcMonth = getSearchRequestAcMonth(startYMD, endYMD);
		return getSelectMaps(wrapper, searchRequestAcMonth, size);
	}

	/**
	 * 通过包装类查询按月分片的索引库集合
	 *
	 * @param wrapper   将要查询的条件封装到wrapper中
	 * @param startDate 开始时间
	 * @param endDate   结束时间
	 * @param size      返回结果集的最大数量
	 * @return 集合 map
	 */
	@Override
	public List<Map<String, Object>> selectMapAcMonth(ESQueryWrapper<T> wrapper, Date startDate, Date endDate, Integer size) {
		SearchRequest searchRequestAcMonth = getSearchRequestAcMonth(startDate, endDate);
		return getSelectMaps(wrapper, searchRequestAcMonth, size);
	}

	/*====================================================================*/
	/*============================== MAP-PAGE ============================*/
	/*====================================================================*/


	/**
	 * 通过包装类查询按月分片的索引库集合
	 *
	 * @param wrapper  将要查询的条件封装到wrapper中
	 * @param date     索引库对应的分片时间
	 * @param pageNum  当前页
	 * @param pageSize 每页容量
	 * @return 分页 map
	 */
	@Override
	public Page<Map<String, Object>> pageMapAcMonth(ESQueryWrapper<T> wrapper, Date date, Integer pageNum, Integer pageSize) {
		SearchRequest searchRequestAcMonth = getSearchRequestAcMonth(date, date);
		return getPageMap(wrapper, pageNum, pageSize, searchRequestAcMonth);
	}

	/**
	 * 通过包装类查询按月分片的索引库集合
	 *
	 * @param wrapper   将要查询的条件封装到wrapper中
	 * @param yearMonth 年月 格式：2020-02 自然月份对应的索引库结尾日期
	 * @param pageNum   当前页
	 * @param pageSize  每页容量
	 * @return 分页 map
	 */
	@Override
	public Page<Map<String, Object>> pageMapAcMonth(ESQueryWrapper<T> wrapper, String yearMonth, Integer pageNum, Integer pageSize) {
		SearchRequest searchRequestAcMonth = getSearchRequestAcMonth(yearMonth, yearMonth);
		return getPageMap(wrapper, pageNum, pageSize, searchRequestAcMonth);
	}

	/**
	 * 通过包装类查询按月分片的索引库集合
	 *
	 * @param wrapper   将要查询的条件封装到wrapper中
	 * @param startDate 开始时间
	 * @param endDate   结束时间
	 * @param pageNum   当前页
	 * @param pageSize  每页容量
	 * @return 分页 map
	 */
	@Override
	public Page<Map<String, Object>> pageMapAcMonth(ESQueryWrapper<T> wrapper, Date startDate, Date endDate, Integer pageNum, Integer pageSize) {
		SearchRequest searchRequestAcMonth = getSearchRequestAcMonth(startDate, endDate);
		return getPageMap(wrapper, pageNum, pageSize, searchRequestAcMonth);
	}

	/**
	 * 通过包装类查询按月分片的索引库集合
	 *
	 * @param wrapper  将要查询的条件封装到wrapper中
	 * @param startYMD 开始时间
	 * @param endYMD   结束时间
	 * @param pageNum  当前页
	 * @param pageSize 每页容量
	 * @return 分页 map
	 */
	@Override
	public Page<Map<String, Object>> pageMapAcMonth(ESQueryWrapper<T> wrapper, String startYMD, String endYMD, Integer pageNum, Integer pageSize) {
		SearchRequest searchRequestAcMonth = getSearchRequestAcMonth(startYMD, endYMD);
		return getPageMap(wrapper, pageNum, pageSize, searchRequestAcMonth);
	}

	/*====================================================================*/
	/*=============================== COUNT ==============================*/
	/*====================================================================*/

	/**
	 * count
	 * 可用于非等查询
	 *
	 * @param wrapper 将要查询的条件封装到ESQueryWrapper中
	 * @param date    日期 决定了查询的索引库
	 * @return
	 */
	@Override
	public Long countAcMonth(ESQueryWrapper<T> wrapper, Date date) {
		CountRequest countRequest = new CountRequest(getIndexNameAcMonth(date));
		return getCount(countRequest, wrapper);
	}

	/**
	 * count
	 * 可用于非等查询
	 *
	 * @param wrapper 将要查询的条件封装到ESQueryWrapper中
	 * @param YM      年月 决定了查询的索引库 yyyy-MM
	 * @return
	 */
	@Override
	public Long countAcMonth(ESQueryWrapper<T> wrapper, String YM) {
		CountRequest countRequest = new CountRequest(getIndexNameAcMonth(YM));
		return getCount(countRequest, wrapper);
	}

	/**
	 * count
	 * 可用于非等查询
	 *
	 * @param wrapper   将要查询的条件封装到ESQueryWrapper中
	 * @param startDate 日期 决定了查询的起始索引库
	 * @param endDate   日期 决定了查询的结束索引库
	 * @return
	 */
	@Override
	public Long countAcMonth(ESQueryWrapper<T> wrapper, Date startDate, Date endDate) {
		CountRequest countRequest = new CountRequest(getIndexNamesAcMonth(startDate, endDate));
		return getCount(countRequest, wrapper);
	}

	/**
	 * count
	 * 可用于非等查询
	 *
	 * @param wrapper 将要查询的条件封装到ESQueryWrapper中
	 * @param startYM 年月 决定了查询的起始索引库 yyyy-MM
	 * @param endYM   年月 决定了查询的结束索引库 yyyy-MM
	 * @return
	 */
	@Override
	public Long countAcMonth(ESQueryWrapper<T> wrapper, String startYM, String endYM) {
		CountRequest countRequest = new CountRequest(getIndexNamesAcMonth(startYM, endYM));
		return getCount(countRequest, wrapper);
	}
}
